#ifndef _chris_poly_jun_7_32_header_guard_
#define _chris_poly_jun_7_32_header_guard_

#include "config.h"
#include <Eigen/StdVector>
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <Eigen/LU>
#include <list>
#include <limits>
#include <algorithm>
#include <Eigen/LU>

namespace libPortal
{

  typedef std::list<Eigen::Vector2d>::const_iterator const_vertex_iter;
  typedef std::list<Eigen::Vector2d>::iterator vertex_iter;

  /**
     This class is just a 3d polygon, for when triangles don't do the trick.
     The last point always connects to the fist. Just to be clear, this class 
     can't represent polygons that are hollow. Also, although the poly is 3d, it's 
     stored as 2d points and transformation. All the polygon points lie on the xy plane, 
     but under the assumed transformation of the matrix. 
   */
  class PORTALAPI Polygon3D
  {
  public:
    /**
       Creates an (invalid) polygon with no points. All points added in the
       future will lie on plane.
       @param plane the 3d plane that the polygon lives in.
     */
    Polygon3D(Eigen::Hyperplane<double, 3> p, double prec = 1e-6);

    /**
       This function cuts out (deletes) all the area of the polygon that is on
       the positive side of the plane defined by plane. This function may 
       change the number and position of points in this class.
       @param plane the equation of a 3d plane
     */
    void cut(Eigen::Hyperplane<double, 3> plane);

    /**
       Returns true if the polygon has at least 3 points, the edges don't 
       overlap and there are no congruent edges.
     */
    bool isValid() const;

    /**
       Returns true if all the exterior angles are >= 180deg.
     */
    bool isConvex() const;

    /**
      Returns the area of the polygon. 0 if it is invalid.
     */
    double calculateArea() const;

    /**
       Returns true if the polygon other intersects this one, false otherise.
       Always false if either of the polygons are invalid.
       @param other the polygon to test against. 
     */
    bool intersects(const Polygon3D &other) const;
    

    /**
       This function adds point p between the first and last points that form 
       the polygon.
       @param p the point to add.
     */
    bool append(Eigen::Vector3d p);

    /**
       Sets the polygon to a huge quad along the plane. 
     */
    void setMaxQuad();

    /**
       Gets the number of vertices in the polygon.
       @return the number of verticies in the polygon. 
     */
    unsigned int numVerts() const;

    /**
       This function takes the polygon and breaks it down into triangles.
       The triangle data is stored in the verts parameter. All data in verts
       is deleted before data from this function is added. The format for the 
       vertex data is as follows: <br>
       index 0 - 1st vertex<br>
       index 1 - 2nd vertex<br>
       index 2 - 3rd vertex<br>
       ---Next triangle---<br>
       index 3 - 1st vertex<br>
       ....<br>
       ....<br>
       There should always be a multiple of 3 verts in the list. <br>
       The vertices in this function are 3d and so they are in world space.
       @param verts the array to hold the verts in.
       @param flower if this is true, when decomposing convex polygons a center is picked
       and all the trangles are built around this center, if false, the convex polygons use
       an arbituary vertex as the center. The flower option is nice because it creates triangles
       that are more uniform in terms of edge lengths.
     */
    void triangleDecomposition(std::vector<Eigen::Vector4d> &verts, bool flower = true) const;

    const Eigen::Hyperplane<double, 3> plane;

    /**
       An iterator for internal data.
     */
    const_vertex_iter begin() const;

    /**
       An iterator for internal data.
     */
    const_vertex_iter end() const;

    /**
       An iterator for internal data.
     */
    vertex_iter begin();
    /**
       An iterator for internal data.
     */
    vertex_iter end();
  private:


   /**
       This function does the work behind triangleDecomposition() for convex
       polygons, so read the docs for that function. 
     */
    void PortalDLLLOCAL convexTriangleDecomposition(std::vector<Eigen::Vector4d> &verts, bool flower) const;
    /**
       Same as convexTriangleDecomposition() but for concave triangles.
     */
    void PortalDLLLOCAL concaveTriangleDecomposition(std::vector<Eigen::Vector4d> &v) const;

    /**
       The data list for this class. first to last in clockwise order.
     */
    std::list<Eigen::Vector2d, Eigen::aligned_allocator<Eigen::Vector2d> > points;
    Eigen::Transform<double, 3> polyTrans;

    /**
       This variable defines the maximum acceptable numerical error for 
       points on the plane, or intersections, etc...
     */
    const double maxErr;
  };
}

#endif
